import { isEmpty, includes, compact, map, has, pick, keys, extend, get, isNumber } from "lodash";
import { useState, useEffect, useMemo, useCallback, useRef } from "react";
import notification from "@/services/notification";
import location from "@/services/location";
import url from "@/services/url";
import { Dashboard, collectDashboardFilters } from "@/services/dashboard";
import { currentUser } from "@/services/auth";
import recordEvent from "@/services/recordEvent";
import { QueryResultError } from "@/services/query";
import AddWidgetDialog from "@/components/dashboards/AddWidgetDialog";
import AddContainerDialog from "@/components/dashboards/AddContainerDialog";
import AddUrlDialog from "@/components/dashboards/AddUrlDialog";
import AddWeatherDialog from "@/components/dashboards/AddWeatherDialog";
import AddVideoDialog from "@/components/dashboards/AddVideoDialog";
import TextboxDialog from "@/components/dashboards/TextboxDialog";
import PermissionsEditorDialog from "@/components/PermissionsEditorDialog";
import { editableMappingsToParameterMappings, synchronizeWidgetTitles } from "@/components/ParameterMappingInput";
import ShareDashboardDialog from "../components/ShareDashboardDialog";
import useFullscreenHandler from "../../../lib/hooks/useFullscreenHandler";
import useRefreshRateHandler from "./useRefreshRateHandler";
import useEditModeHandler from "./useEditModeHandler";
import { policy } from "@/services/policy";

export { DashboardStatusEnum } from "./useEditModeHandler";

function getAffectedWidgets(dashboard_id, widgets, updatedParameters = []) {
  var times = 0;
  if (localStorage.getItem("refresh_" + dashboard_id)) times = parseInt(localStorage.getItem("refresh_" + dashboard_id)) + 1;
  localStorage.setItem("refresh_" + dashboard_id, times);

  //视图部件不随报表刷新或慢倍数刷新
  widgets = widgets.filter(widget => {
    if (widget.options && widget.options.widgetOptions) {
      if (!!widget.options.widgetOptions.no_refresh) return false;
      if (widget.options.widgetOptions.rate_refresh) {
        if (times % parseInt(widget.options.widgetOptions.rate_refresh) !== 0) return false;
      }
    }
    return true;
  });
  
  return !isEmpty(updatedParameters)
    ? widgets.filter(widget =>
        Object.values(widget.getParameterMappings())
          .filter(({ type }) => type === "dashboard-level")
          .some(({ mapTo }) =>
            includes(
              updatedParameters.map(p => p.name),
              mapTo
            )
          )
      )
    : widgets;
}

function useDashboard(dashboardData, isContainer = false, isPop = false) {
  const [dashboard, setDashboard] = useState(dashboardData);
  const [filters, setFilters] = useState([]);
  const [refreshing, setRefreshing] = useState(false);
  const [gridDisabled, setGridDisabled] = useState(false);
  const globalParameters = useMemo(() => dashboard.getParametersDefs(), [dashboard]);
  const canEditDashboard = !dashboard.is_archived && policy.canEdit(dashboard);
  const isDashboardOwnerOrAdmin = useMemo(
    () =>
      !dashboard.is_archived &&
      has(dashboard, "user.id") &&
      (currentUser.id === dashboard.user.id || currentUser.isAdmin),
    [dashboard]
  );
  // const hasOnlySafeQueries = useMemo(
  //   () => every(dashboard.widgets, w => (w.getQuery() ? w.getQuery().is_safe : true)),
  //   [dashboard]
  // );

  const managePermissions = useCallback(() => {
    const aclUrl = `api/dashboards/${dashboard.id}/acl`;
    PermissionsEditorDialog.showModal({
      aclUrl,
      context: "dashboard",
      author: dashboard.user,
    });
  }, [dashboard]);

  const updateDashboard = useCallback(
    (data, includeVersion = true) => {
      setDashboard(currentDashboard => extend({}, currentDashboard, data));
      data = { ...data, id: dashboard.id };
      if (includeVersion) {
        data = { ...data, version: dashboard.version };
      }
      return Dashboard.save(data)
        .then(updatedDashboard => {
          setDashboard(currentDashboard => extend({}, currentDashboard, pick(updatedDashboard, keys(data))));
          if (has(data, "name")) {
            location.setPath(url.parse(updatedDashboard.url).pathname, true);
          }
        })
        .catch(error => {
          const status = get(error, "response.status");
          if (status === 403) {
            notification.error("Dashboard update failed", "Permission Denied.");
          } else if (status === 409) {
            notification.error(
              "报表被另外的用户变更了。",
              "请备份信息重新加载。",
              { duration: null }
            );
          }
        });
    },
    [dashboard]
  );

  const togglePublished = useCallback(() => {
    recordEvent("toggle_published", "dashboard", dashboard.id);
    updateDashboard({ is_draft: !dashboard.is_draft }, false);
  }, [dashboard, updateDashboard]);

  const loadWidget = useCallback((widget, visualizationId = 0, forceRefresh = false) => {
    // 切换视图;
    if (!!visualizationId && visualizationId !== 0 && visualizationId !== widget.visualization.id && widget.visualizations) {
      if (!widget.hasOwnProperty("visualization_origin")) {
        widget.visualization_origin = widget.visualization;
      }
      widget.visualization = widget.visualizations[visualizationId];
      if (!!widget.visualization.query_id && widget.visualization.query_id !== widget.query.id) {
        widget.name = widget.visualization.name;
        widget.query_id = widget.visualization.query_id;
        widget.query = undefined;
        widget.queryResult = undefined;
        widget.$parameters = null;
        forceRefresh = true;
      }
    }

    widget.getParametersDefs(); // Force widget to read parameters values from URL
    setDashboard(currentDashboard => extend({}, currentDashboard));

    var max_age, widgetMaxAge;
    const { maxAge } = location.search;    
    if (widget.options && widget.options.widgetOptions && widget.options.widgetOptions.queriesMaxAge) 
      widgetMaxAge =  widget.options.widgetOptions.queriesMaxAge;
    else if (dashboard.layout && !!dashboard.layout.queriesMaxAge)
      widgetMaxAge =  dashboard.layout.queriesMaxAge;

    if (!!maxAge) 
      max_age = parseInt(maxAge)
    else if (!!widgetMaxAge)
      max_age = parseInt(widgetMaxAge)

    if (!max_age && typeof max_age !== "undefined" && max_age !== 0) max_age = undefined;

    return widget.load(forceRefresh, max_age).catch(error => {
        if (error instanceof QueryResultError) {
          return;
        }
        return Promise.reject(error);
    }).finally(() => 
      setDashboard(currentDashboard => extend({}, currentDashboard))
    );
  }, [dashboard]);

  //整形：指定部件id;字符串：部件id1,部件id2,……；对象：指定部件对象
  const refreshWidget = useCallback((widget, visualizationId=0, forceRefresh = true) => {
    sessionStorage.clear();
    if (typeof widget === "string" ) {
      widget.split(",").forEach((key)=>{
        dashboard.widgets.forEach((wid)=>{
          if (wid.id === parseInt(key)) return loadWidget(wid, visualizationId, forceRefresh);
      })
      })
    } else if (isNumber(widget)) {
      dashboard.widgets.forEach((wid)=>{
        if (wid.id === widget) return loadWidget(wid, visualizationId, forceRefresh);
      })
    } else {
      return loadWidget(widget, visualizationId, forceRefresh);
    }
  }, [dashboard, loadWidget]);

  const removeWidget = useCallback(widgetId => {
    setDashboard(currentDashboard =>
      extend({}, currentDashboard, {
        widgets: currentDashboard.widgets.filter(widget => widget.id !== undefined && widget.id !== widgetId),
      })
    );
  }, []);

  const AddTemplateWidget = useCallback(widget => {
    setDashboard(currentDashboard =>
      extend({}, currentDashboard, {
        widgets: [...currentDashboard.widgets, widget],
      })
    );
  }, []);

  const EditTemplateWidget = useCallback(widget => {
    setDashboard(currentDashboard =>
      extend({}, currentDashboard, {
        widgets: [...map(currentDashboard.widgets,w=>w.id===widget.id ? widget : w)],
      })
    );
  }, []);

  const dashboardRef = useRef();
  dashboardRef.current = dashboard;

  const loadDashboard = useCallback((forceRefresh = false, updatedParameters = []) => {
    //重新加载清空刷新次数
    if (!forceRefresh) localStorage.removeItem("refresh_" + dashboardRef.current.id)
    // sessionStorage.clear();
    const affectedWidgets = getAffectedWidgets(dashboardRef.current.id, dashboardRef.current.widgets, updatedParameters);
      const loadWidgetPromises = compact(
      affectedWidgets.map(widget => loadWidget(widget, 0, forceRefresh).catch(error => error))
      );

      return Promise.all(loadWidgetPromises).then(() => {
        const queryResults = compact(map(dashboardRef.current.widgets, widget => widget.getQueryResult()));
        const updatedFilters = collectDashboardFilters(dashboardRef.current, queryResults, location.search);
        setFilters(updatedFilters);
      });
  },[loadWidget]);

  const refreshDashboard = useCallback(
    updatedParameters => {
      //解决内容点击主动刷新
      var isForce = false;
      if (!!updatedParameters && typeof updatedParameters === "boolean") {
        isForce = updatedParameters;
        updatedParameters = undefined;
      }

      if (!refreshing || isForce) {
        setRefreshing(true);
        sessionStorage.clear();
        loadDashboard(true, updatedParameters).finally(() => setRefreshing(false));
      }
    },
    [refreshing, loadDashboard]
  );

  const archiveDashboard = useCallback(() => {
    recordEvent("archive", "dashboard", dashboard.id);
    Dashboard.delete(dashboard).then(updatedDashboard =>
      setDashboard(currentDashboard => extend({}, currentDashboard, pick(updatedDashboard, ["is_archived"])))
    );
  }, [dashboard]); // eslint-disable-line react-hooks/exhaustive-deps

  const removeDashboard = useCallback(() => {
    recordEvent("remove", "dashboard", dashboard.id);
    Dashboard.remove(dashboard).then();
  }, [dashboard]); // eslint-disable-line react-hooks/exhaustive-deps

  const showShareDashboardDialog = useCallback(() => {
    // const handleDialogClose = () => setDashboard(currentDashboard => extend({}, currentDashboard));
    const handleDialogClose = () => window.location.reload();

    ShareDashboardDialog.showModal({
      dashboard,
      hasOnlySafeQueries: true,
    })
      .onClose(handleDialogClose)
      .onDismiss(handleDialogClose);
  }, [dashboard]);

  const showAddTextboxDialog = useCallback(() => {
    TextboxDialog.showModal({
      isNew: true,
    }).onClose(({text, widgetOptions}) => {
      dashboard.addWidget(text, {widgetOptions: widgetOptions,}).then(() => setDashboard(currentDashboard => extend({}, currentDashboard)));
      document.documentElement.style.background = "#f6f8f9";
    });
  }, [dashboard]);

  const showAddContainerDialog = useCallback(() => {
    AddContainerDialog.showModal({dashboard,}).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard.addWidget(visualization, {
        parameterMappings: editableMappingsToParameterMappings(parameterMappings),
        widgetOptions: widgetOptions,
      }).then(widget => {
        const widgetsToSave = [
          widget,
          ...synchronizeWidgetTitles(widget.options.parameterMappings, dashboard.widgets),
        ];
        return Promise.all(widgetsToSave.map(w => w.save())).then(() =>
          setDashboard(currentDashboard => extend({}, currentDashboard))
        );
      })
    );
  }, [dashboard]);

  const showEditContainerDialog = useCallback((widget) => {
    AddContainerDialog.showModal({
      dashboard, widget
    }).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard
        .editWidget(widget, visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
        })
        .then(wid => {
          const widgetsToSave = [
            ...synchronizeWidgetTitles(wid.options.parameterMappings, dashboard.widgets),
          ];

          if (widgetsToSave.length === 0) 
          {
            window.location.reload();
            return;
          }

          return Promise.all(widgetsToSave.map(w => w.save())).then(() => {
            setDashboard(currentDashboard => extend({}, currentDashboard));
            window.location.reload();
          });
        })
    );
  }, [dashboard]);

  const showAddUrlDialog = useCallback(() => {
    AddUrlDialog.showModal({dashboard,}).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard.addWidget(visualization, {
        parameterMappings: editableMappingsToParameterMappings(parameterMappings),
        widgetOptions: widgetOptions,
      }).then(widget => {
        const widgetsToSave = [
          widget,
          ...synchronizeWidgetTitles(widget.options.parameterMappings, dashboard.widgets),
        ];
        return Promise.all(widgetsToSave.map(w => w.save())).then(() =>
          setDashboard(currentDashboard => extend({}, currentDashboard))
        );
      })
    );
  }, [dashboard]);

  const showEditUrlDialog = useCallback((widget) => {
    AddUrlDialog.showModal({
      dashboard, widget
    }).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard
        .editWidget(widget, visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
        })
        .then(wid => {
          const widgetsToSave = [
            ...synchronizeWidgetTitles(wid.options.parameterMappings, dashboard.widgets),
          ];

          if (widgetsToSave.length === 0) 
          {
            window.location.reload();
            return;
          }

          return Promise.all(widgetsToSave.map(w => w.save())).then(() => {
            setDashboard(currentDashboard => extend({}, currentDashboard));
            window.location.reload();
          });
        })
    );
  }, [dashboard]);

  const showAddWeatherDialog = useCallback(() => {
    AddWeatherDialog.showModal({dashboard,}).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard.addWidget(visualization, {
        parameterMappings: editableMappingsToParameterMappings(parameterMappings),
        widgetOptions: widgetOptions,
      }).then(widget => {
        const widgetsToSave = [
          widget,
          ...synchronizeWidgetTitles(widget.options.parameterMappings, dashboard.widgets),
        ];
        return Promise.all(widgetsToSave.map(w => w.save())).then(() =>
          setDashboard(currentDashboard => extend({}, currentDashboard))
        );
      })
    );
  }, [dashboard]);

  const showEditWeatherDialog = useCallback((widget) => {
    AddWeatherDialog.showModal({
      dashboard, widget
    }).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard
        .editWidget(widget, visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
        })
        .then(wid => {
          const widgetsToSave = [
            ...synchronizeWidgetTitles(wid.options.parameterMappings, dashboard.widgets),
          ];

          if (widgetsToSave.length === 0) 
          {
            window.location.reload();
            return;
          }

          return Promise.all(widgetsToSave.map(w => w.save())).then(() => {
            setDashboard(currentDashboard => extend({}, currentDashboard));
            window.location.reload();
          });
        })
    );
  }, [dashboard]);

  const showAddVideoDialog = useCallback(() => {
    AddVideoDialog.showModal({dashboard,}).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard.addWidget(visualization, {
        parameterMappings: editableMappingsToParameterMappings(parameterMappings),
        widgetOptions: widgetOptions,
      }).then(widget => {
        const widgetsToSave = [
          widget,
          ...synchronizeWidgetTitles(widget.options.parameterMappings, dashboard.widgets),
        ];
        return Promise.all(widgetsToSave.map(w => w.save())).then(() =>
          setDashboard(currentDashboard => extend({}, currentDashboard))
        );
      })
    );
  }, [dashboard]);

  const showEditVideoDialog = useCallback((widget) => {
    AddVideoDialog.showModal({
      dashboard, widget
    }).onClose(({ visualization, parameterMappings, widgetOptions }) =>
      dashboard
        .editWidget(widget, visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
        })
        .then(wid => {
          const widgetsToSave = [
            ...synchronizeWidgetTitles(wid.options.parameterMappings, dashboard.widgets),
          ];

          if (widgetsToSave.length === 0) 
          {
            window.location.reload();
            return;
          }

          return Promise.all(widgetsToSave.map(w => w.save())).then(() => {
            setDashboard(currentDashboard => extend({}, currentDashboard));
            window.location.reload();
          });
        })
    );
  }, [dashboard]);

  const showAddWidgetDialog = useCallback(() => {
    AddWidgetDialog.showModal({
      dashboard,
    }).onClose(({ visualization, parameterMappings, widgetOptions, position }) =>
      dashboard
        .addWidget(visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
          position: position,
        })
        .then(widget => {
          const widgetsToSave = [
            widget,
            ...synchronizeWidgetTitles(widget.options.parameterMappings, dashboard.widgets),
          ];
          return Promise.all(widgetsToSave.map(w => w.save())).then(() =>
            setDashboard(currentDashboard => extend({}, currentDashboard))
          );
        })
    );
  }, [dashboard]);

  const showEditWidgetDialog = useCallback((widget) => {
    AddWidgetDialog.showModal({
      dashboard, widget
    }).onClose(({ visualization, parameterMappings, widgetOptions, position }) =>
      dashboard
        .editWidget(widget, visualization, {
          parameterMappings: editableMappingsToParameterMappings(parameterMappings),
          widgetOptions: widgetOptions,
          position: position,
        })
        .then(wid => {
          const widgetsToSave = [
            ...synchronizeWidgetTitles(wid.options.parameterMappings, dashboard.widgets),
          ];

          if (widgetsToSave.length === 0) 
          {
            window.location.reload();
            return;
          }

          return Promise.all(widgetsToSave.map(w => w.save())).then(() => {
            setDashboard(currentDashboard => extend({}, currentDashboard));
            window.location.reload();
          });
        })
    );
  }, [dashboard]);

  const [refreshRate, setRefreshRate, disableRefreshRate] = useRefreshRateHandler(refreshDashboard);
  const [fullscreen, toggleFullscreen] = useFullscreenHandler();
  const editModeHandler = useEditModeHandler(!gridDisabled && canEditDashboard, dashboard.widgets);

  useEffect(() => {
    setDashboard(dashboardData);
    loadDashboard();
  }, [dashboardData]); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    if (!isContainer && !isPop) document.title = dashboard.name;
  }, [dashboard.name, isContainer, isPop]);

  // reload dashboard when filter option changes
  useEffect(() => {
    loadDashboard();
  }, [dashboard.dashboard_filters_enabled]); // eslint-disable-line react-hooks/exhaustive-deps

  return {
    dashboard,
    globalParameters,
    refreshing,
    filters,
    setFilters,
    loadDashboard,
    refreshDashboard,
    updateDashboard,
    togglePublished,
    archiveDashboard,
    removeDashboard,
    loadWidget,
    refreshWidget,
    removeWidget,
    canEditDashboard,
    isDashboardOwnerOrAdmin,
    refreshRate,
    setRefreshRate,
    disableRefreshRate,
    ...editModeHandler,
    gridDisabled,
    setGridDisabled,
    fullscreen,
    toggleFullscreen,
    showShareDashboardDialog,
    showAddTextboxDialog,
    showAddContainerDialog,
    showAddUrlDialog,
    showAddWeatherDialog,
    showAddVideoDialog,
    showAddWidgetDialog,
    showEditWidgetDialog,
    showEditContainerDialog,
    showEditUrlDialog,
    showEditWeatherDialog,
    showEditVideoDialog,
    AddTemplateWidget,
    EditTemplateWidget,
    managePermissions,
  };
}

export default useDashboard;
